/**
 * @method Countdown 倒计时解决方案
 * @author Lkx
 *
 * @method use       启用倒计时
 * @method query     查询倒计时
 * @method clear     清理指定倒计时
 * @method clear_all 清理全部倒计时
 */

(function (factory) {

  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = factory();
  } else {
    window.Kxui.countdown = window.kxui.countdown = factory();
  }

}(function () {

  "use strict";

  const win = window;
  const Kxui = win.Kxui;
  const method = Kxui.method;

  // 全局错误样式
  // 通过主入口注册，达到全模块统一错误输出
  const Kxui_throws_style = win.Kxui_throws_style;

  /**
   * @method 异常抛出
   * @param  {number} error     抛出错误的类型[警告(0)/错误(1)]
   * @param  {number} num       抛出错误文案的编号
   * @param  {string} parameter 抛出错误的参数
   * @param  {string} info      其他说明信息
   */
  const throws = (error, num, parameter, info) => {
    if (Kxui.configuration.debug) {
      const nums = {};
      nums[0] = `依赖模块 {${parameter}} 未被提前加载`;
      nums[1] = `配置参数 {${parameter}} 为空或数据类型错误，正确数据类型应为 {${info}}`;
      nums[2] = `配置参数 {stamp} 与 {seconds} 同时存在时，将取 {stamp} 做为倒计时依据`;
      const text = [`%cKxui.countdown%c (${Kxui.about.version})\n%c${nums[num]}，请检查修改。`, Kxui_throws_style[0], Kxui_throws_style[1], Kxui_throws_style[2]];
      error === 0 ? console.warn(...text) : console.error(...text);
    }
  };

  // 前置依赖判断
  // 判断当前模块是否前置依赖均加载完毕
  const pre_dependence = () => {
    if (method) {
      return true;
    } else {
      throws(1, 0, "method");
      return false;
    }
  };

  class Load {

    constructor(parameter) {
      const is_seconds = method.data(parameter.seconds, "number", true);
      const is_stamp = method.data(parameter.stamp, "number", true);
      const is_iden = method.data(parameter.iden, "string");
      const is_callback = method.data(parameter.callback, "function");
      const is_callback_final = parameter.type === "query" ? is_iden ? is_callback : method.data(parameter.iden, "function") : is_callback;

      if (!is_seconds && !is_stamp && parameter.type === "use") {
        throws(1, 1, "seconds/stamp", "number");
      } else if (!is_callback_final) {
        throws(1, 1, "callback", "function");
      } else {

        // 容错警告
        // seconds 与 stamp 只能存在一种
        // 当两个参数同时存在时，将取 stamp 做为倒计时依据
        if (is_seconds && is_stamp) {
          throws(0, 2);
        }

        this.is_iden = is_iden;
        this.is_stamp = is_stamp;
        this.is_callback = is_callback;
        this.bifurcation(parameter);
      }
    }

    // 逻辑路口
    // 判断是根据秒倒计时还是根据时间戳进行倒计时
    bifurcation(parameter) {
      if (this.is_stamp) {
        const date_sta = method.date_get();
        const date_end = String(parameter.stamp).length === 10 ? method.num_acc_mul(parameter.stamp, 1000) : parameter.stamp;
        const callback = parameter.callback;
        if (date_sta >= date_end) {
          callback(false);
          return;
        }

        this.callback = callback;
        this.seconds = Math.floor(method.num_acc_div(method.num_acc_sub(date_end, date_sta), 1000));
        this.enable_timer();
      } else {
        this.iden = this.is_iden ? parameter.iden : "default";
        this.type = parameter.type;
        this.seconds = parameter.seconds;
        this.callback = this.is_callback ? parameter.callback : parameter.iden;

        // 在使用秒进行倒计时时，时候具备连续性
        // 连续性的意思在于下次进入页面时将会继续执行上次未能执行完的倒计时
        this.is_continuity = method.data(parameter.continuity, "boolean") ? parameter.continuity : true;
        if (this.is_continuity) {
          this.pre_cleaning();
        } else {
          method.sto_cache_del(`[kxui/countdown/${this.iden}]`);
          this.enable_timer();
        }
      }
    }

    // 前置初始化
    // 初始化清理计时器
    // 初始化计时器回调
    pre_cleaning() {
      this.data_assemble = method.sto_cache_get(`[kxui/countdown/${this.iden}]`) || {};

      const date = method.date_get();
      const time_r = method.data_defense(this.data_assemble, ["time_r"]);
      const time_e = method.data_defense(this.data_assemble, ["time_e"]);
      if (time_r) { clearInterval(time_r); }

      if (time_e >= date) {
        this.seconds = Math.floor(method.num_acc_div(method.num_acc_sub(time_e, date), 1000));
      } else {
        this.data_assemble = {};
        method.sto_cache_del(`[kxui/countdown/${this.iden}]`);

        if (this.type === "query") {
          this.callback(false);
          return;
        }
      }

      this.enable_timer();
      this.assemble();
    }

    // 启动倒计时计时器
    enable_timer() {
      this.callback_handle();
      this.timer = setInterval(() => {
        this.seconds = this.seconds - 1;
        if (this.seconds <= 0) {
          this.seconds = false;
          clearInterval(this.timer);

          // 针对使用 seconds 进行倒计时进行清空
          // 针对 continuity 为 true 的状态下进行清空
          if (!this.is_stamp && this.is_continuity) {
            method.sto_cache_del(`[kxui/countdown/${this.iden}]`);
          }

          this.callback(false);
        } else {
          this.callback_handle();
        }
      }, 1000);
    }

    // 数据组装并储存
    // 储存带有标识的计时器，以便及时进行停止
    // 储存时间戳关系，当页面二次或多次进入时，判断是否脱离计时状态
    // 此方法只会在倒计时状态为秒时候进入，即使用 seconds 字段进行倒计时
    assemble() {
      const data = {
        time_r: this.timer,
        time_e: method.data_defense(this.data_assemble, ["time_e"]) || method.num_acc_add(method.date_get(), method.num_acc_mul(this.seconds, 1000))
      };
      method.sto_cache_set(`[kxui/countdown/${this.iden}]`, data);
    }

    // 回调处理
    // 用于数据回调组装类型
    callback_handle() {
      const data = {};
      data.format = method.date_format_seconds(this.seconds);
      data.seconds = this.seconds;
      this.callback(data);
    }
  }

  class Countdown {

    constructor() {
      this.name = "Countdown";
      this.info = "Countdown Solution";
    }

    /**
     * @method 启用倒计时
     * @param  {object} parameter 倒计时参数
     *
     * Countdown.use(parameter)
     * 可通过此方法进行启用倒计时
     * 倒计时将会根据 parameter.seconds 设定的时长进行
     * 倒计时根据 parameter.iden 来区分，意味着同页面可同时进行倒数多个验证码
     */
    use(parameter) {
      if (pre_dependence()) {
        const is_parameter_type = typeof parameter === "object";
        if (is_parameter_type) {
          parameter.type = "use";
          new Load(parameter);
        } else {
          throws(1, 1, "parameter", "object");
        }
      }
    }

    /**
     * @method 查询倒计时
     * @param  {string}   iden     当前启动标识码
     * @param  {function} callback 倒计时回调
     *
     * Countdown.query(iden, callback)
     * 可通过此方法进行查询倒计时
     * 倒计时根据 iden 来进行查询，逻辑同 use 一致，也将会返回实时状态
     */
    query(iden, callback) {
      if (pre_dependence()) {
        const parameter = {};
        parameter.iden = iden;
        parameter.type = "query";
        parameter.callback = callback;
        new Load(parameter);
      }
    }

    /**
     * @method 清理指定倒计时
     * @param  {string/function} iden     当前启动标识码/清理完成后回调
     * @param  {function}        callback 清理完成后回调
     *
     * Countdown.clear(iden, callback)
     * 可通过此方法进行手动清理倒计时
     * 当不传 iden 时，将会使用默认值 default
     */
    clear(iden, callback) {
      if (pre_dependence()) {
        const is_iden = method.data(iden, "string");
        const is_iden_fun = method.data(iden, "function");
        const is_callback = method.data(callback, "function");

        const name = is_iden ? iden : "default";
        const tiemr = method.sto_cache_get(`[kxui/countdown/${name}]`) || {};
        clearInterval(tiemr.time_r);
        method.sto_cache_del(`[kxui/countdown/${name}]`);

        if (!is_callback && is_iden_fun) {
          iden();
        } else if (is_callback) {
          callback();
        }
      }
    }

    /**
     * @method 清理全部倒计时
     * @param  {function} callback 清理完成后回调
     *
     * Countdown.clear_all()
     * 可通过此方法进行手动清理全部倒计时
     */
    clear_all(callback) {
      if (pre_dependence()) {
        const local = win.localStorage;
        const is_callback = method.data(callback, "function");
        for (let name in local) {
          if (name.indexOf("[kxui/countdown/") >= 0) {
            const tiemr = method.sto_cache_get(name) || {};
            clearInterval(tiemr.time_r);
            method.sto_cache_del(name);
          }
        }

        if (is_callback) { callback(); }
      }
    }
  }

  return new Countdown();

}));
